// SPDX-License-Identifier: GPL-2.0
/* watchdog-test.c - watchdog test driver
 *
 * Copyright (C) 2016-2018, LomboTech Co.Ltd.
 * Author: lomboswer <lomboswer@lombotech.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#define DEBUG

#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/err.h>
#include <linux/sched.h>
#include <linux/kthread.h>
#include <linux/delay.h>

#include "linux/watchdog.h"
#include "common.h"

/* redefine mode name, for PTR_XXX */
#undef MOD_NAME
#define MOD_NAME	"WDT_TEST"

/*
 * timeout:
 *	n7v1: 0 ~ 16 second
 *	other: 0 ~ 524 second
 */
#define FEED_TIME	(5)
#define WD_TIMEOUT	(10)
#define FEED_COUNT	(5)

int feed_thread(void *data)
{
	int count = 0;
	struct file *pfile = data;
	loff_t pos = pfile->f_pos;
	unsigned char buf[] = "65535";

	while (1) {
		if (kthread_should_stop()) {
			PRT_INFO("feed dog thread stop ...\n");
			break;
		}

		count++;
		PRT_INFO("%ds...\n", count);

		if ((count % FEED_TIME) == 0) {
			/* feed dog */
			PRT_INFO("Feed the dog!\n");
			vfs_write(pfile, buf, sizeof(buf), &pos);
			count = 0;
		}

		ssleep(1);
	}

	return 0;
}

void watchdog_test(void)
{
	static struct task_struct *test_task;
	struct file *pfile;
	struct watchdog_info wi;
	int tmp;
	int timeout;
	int i = WD_TIMEOUT;
	int ret = 0;

#if !defined(CONFIG_ARCH_LOMBO_N7V1)
	timeout = WD_TIMEOUT * 1000;
#else
	timeout = WD_TIMEOUT;
#endif

	/* open watchdog device */
	pfile = filp_open("/dev/watchdog", O_RDWR | O_NONBLOCK, 0);
	if (IS_ERR(pfile)) {
		PRT_ERR("open watchdog dev failed\n");
		return;
	}

	/* get watcdog info */
	vfs_ioctl(pfile, WDIOC_GETSUPPORT, (unsigned long)&wi);
	PRT_INFO("watchdog info:%d, %s\n", wi.options, wi.identity);

	/* set watchdog timeout, timeout > FEED_TIME */
	PRT_INFO("set watchdog timeout = %d\n", timeout);
	tmp = timeout;
	ret = vfs_ioctl(pfile, WDIOC_SETTIMEOUT, (unsigned long)&tmp);
	if (ret) {
		PRT_INFO("set timeout failed,err code %d\n", ret);
		return;
	}

	/* enable */
	PRT_INFO("enable watchdog.\n");
	tmp = WDIOS_ENABLECARD;
	vfs_ioctl(pfile, WDIOC_SETOPTIONS, (unsigned long)&tmp);

	/* create a thread to feed dog */
	test_task = kthread_create(feed_thread, pfile, "wd_test_task");
	if (IS_ERR(test_task)) {
		PRT_INFO("kthread_create failed\n");

		return;
	}
	PRT_INFO("Create feed dog thread, system will not restart\n");
	wake_up_process(test_task);

	PRT_INFO("wait...\n");
	ssleep(FEED_COUNT * FEED_TIME);

	/* stop feed thread and disable watchdog */
	PRT_INFO("stop feed dog !\n");
	kthread_stop(test_task);

	PRT_INFO("disable watchdog !\n");
	tmp = WDIOS_DISABLECARD;
	vfs_ioctl(pfile, WDIOC_SETOPTIONS, (unsigned long)&tmp);

	PRT_INFO("watdog disable,system will not restart .\n");
	PRT_INFO("wait double timeout...\n");
	ssleep(WD_TIMEOUT * 2);

	PRT_INFO("enable watchdog\n");
	tmp = WDIOS_ENABLECARD;
	vfs_ioctl(pfile, WDIOC_SETOPTIONS, (unsigned long)&tmp);

	/* system will restart if test success */
	PRT_INFO("stop feed dog, wait system restart ...\n");
	do {
		vfs_ioctl(pfile, WDIOC_GETTIMELEFT, (unsigned long)&timeout);
		PRT_INFO("%d ms system will restart\n", timeout);
		ssleep(1);
	} while (i--);

	/* test failed */
	kthread_stop(test_task);
	test_task = NULL;

	filp_close(pfile, NULL);

	PRT_ERR("watchdog test failed!\n");
}
